#ifndef __SMARTPOINTER_H__
#define __SMARTPOINTER_H__

#include "BasicType/BasicType_1.h"
#include <hash_map>

namespace GCRE {
	template <class T>
	class SmartPointer {
		public:
			SmartPointer();
			SmartPointer( T* pT );
			SmartPointer( const SmartPointer<T>& ptr );
			~SmartPointer();

			SmartPointer<T>& operator=( const SmartPointer<T>& ptr );
			SmartPointer<T>& operator=( T* pT );
			operator bool() const;
			operator T*() const;
			BasicType::SFBool operator==( const T* pT ) const;
			BasicType::SFBool operator!=( const T* pT ) const;
			BasicType::SFBool operator==( const SmartPointer<T>& ptr ) const;
			BasicType::SFBool operator!=( const SmartPointer<T>& ptr ) const;
			BasicType::SFBool operator!() const;
			friend BasicType::SFBool operator==( const T* pT, const SmartPointer<T>& ptr );
			friend BasicType::SFBool operator!=( const T* pT, const SmartPointer<T>& ptr );
			T& operator*() const;
			T* operator->() const;
			T* GetPointer() const;

		protected:

		private:
			class Count;
			Count* m_pCount;

			static std::hash_map<void*, void*> mapPtrToCount;
	};

	template <class T>
	std::hash_map<void*, void*> SmartPointer<T>::mapPtrToCount;

	template<class T>
	class SmartPointer<T>::Count {
		public:
			Count( T* pT );
			~Count();

			BasicType::SFUInt IncreaseRef();
			BasicType::SFUInt DecreaseRef();
			T* GetPointer() const;
		private:
			T* const m_ptr;
			BasicType::SFUInt m_uCount;
	};

	template <class T>
	SmartPointer<T>::Count::Count( T* pT ) : 
	m_ptr( pT ), m_uCount( 0 )
	{
		assert( pT != NULL );
	}

	template <class T>
	SmartPointer<T>::Count::~Count()
	{
		assert( 0 == m_uCount );
		delete m_ptr;
	}

	template <class T>
	inline BasicType::SFUInt SmartPointer<T>::Count::IncreaseRef()
	{
		return ++m_uCount;
	}

	template <class T>
	inline BasicType::SFUInt SmartPointer<T>::Count::DecreaseRef()
	{
		assert( 0 != m_uCount );
		return --m_uCount;
	}

	template<class T>
	inline T* SmartPointer<T>::Count::GetPointer() const
	{
		return m_ptr;
	}

	template <class T>
	SmartPointer<T>::SmartPointer() : 
	m_pCount( NULL )
	{

	}

	template <class T>
	SmartPointer<T>::SmartPointer( T* pT ) : 
	m_pCount( NULL )
	{
		if ( NULL != pT )
		{
			if ( mapPtrToCount.find( pT ) == mapPtrToCount.end() )
				mapPtrToCount[pT] = new Count( pT );
			m_pCount = static_cast<Count*>(mapPtrToCount[pT]);
			m_pCount->IncreaseRef();
		}
	}

	template <class T>
	SmartPointer<T>::SmartPointer( const SmartPointer<T>& ptr ) : 
	m_pCount( NULL )
	{
		m_pCount = ptr.m_pCount;
		if ( NULL != m_pCount )
			m_pCount->IncreaseRef();
	}

	template <class T>
	SmartPointer<T>::~SmartPointer()
	{
		if ( m_pCount && m_pCount->DecreaseRef() == 0 )
		{
			mapPtrToCount.erase( m_pCount->GetPointer() );
			delete m_pCount;
		}
		m_pCount = NULL;
	}

	template <class T>
	SmartPointer<T>& SmartPointer<T>::operator=( const SmartPointer<T>& ptr )
	{
		if ( m_pCount && m_pCount->DecreaseRef() == 0 )
		{
			mapPtrToCount.erase( m_pCount->GetPointer() );
			delete m_pCount;
		}
		m_pCount = ptr.m_pCount;
		if ( NULL != m_pCount )
			m_pCount->IncreaseRef();
		return *this;
	}

	template <class T>
	SmartPointer<T>& SmartPointer<T>::operator=( T* pT )
	{
		if ( m_pCount && m_pCount->DecreaseRef() == 0 )
		{
			mapPtrToCount.erase( m_pCount->GetPointer() );
			delete m_pCount;
		}
		m_pCount = NULL;
		if ( NULL != pT )
		{
			if ( mapPtrToCount.find( pT ) == mapPtrToCount.end() )
				mapPtrToCount[pT] = new Count( pT );
			m_pCount = static_cast<Count*>(mapPtrToCount[pT]);
			m_pCount->IncreaseRef();
		}
		return *this;
	}

	template <class T>
	inline T& SmartPointer<T>::operator*() const
	{
		assert( NULL != m_pCount );
		return *(m_pCount->m_ptr);
	}

	template <class T>
	inline T* SmartPointer<T>::operator->() const
	{
		assert( NULL != m_pCount );
		return m_pCount->GetPointer();
	}

	template <class T>
	inline T* SmartPointer<T>::GetPointer() const
	{
		return m_pCount->GetPointer();
	}

	template <class T>
	inline BasicType::SFBool SmartPointer<T>::operator!=( const SmartPointer<T>& ptr ) const
	{
		return m_pCount != ptr.m_pCount;
	}

	template <class T>
	inline BasicType::SFBool SmartPointer<T>::operator==( const SmartPointer<T>& ptr ) const
	{
		return m_pCount == ptr.m_pCount;
	}

	template <class T>
	inline BasicType::SFBool SmartPointer<T>::operator!=( const T* pT ) const
	{
		return pT != m_pCount->GetPointer();
	}

	template <class T>
	inline BasicType::SFBool SmartPointer<T>::operator==( const T* pT ) const
	{
		return pT == m_pCount->GetPointer();
	}

	template <class T>
	inline BasicType::SFBool operator!=( const T* pT, const SmartPointer<T>& ptr )
	{
		return ptr != pT;
	}

	template <class T>
	inline BasicType::SFBool operator==( const T* pT, const SmartPointer<T>& ptr )
	{
		return ptr == pT;
	}

	template <class T>
	inline SmartPointer<T>::operator bool() const
	{
		return ( m_pCount != NULL && m_pCount->GetPointer() != NULL );
	}

	template <class T>
	inline BasicType::SFBool SmartPointer<T>::operator!() const
	{
		return ( m_pCount == NULL || m_pCount->GetPointer() == NULL );
	}

	template <class T>
	inline SmartPointer<T>::operator T*() const
	{
		return ( m_pCount == NULL ? NULL : m_pCount->GetPointer() );
	}

}

#endif	//	__SMARTPOINTER_H__